#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setFixedSize(QPixmap{bg_path.arg(1)}.size());

    connect(&timer, SIGNAL(timeout()), this, SLOT(frame()));
    player = new Object::Player {QRect{QPoint{0,0},bg.size()}};
    boss = new Object::Boss {QRect{QPoint{0,0},bg.size()}};
    set_level(1);
    timer.start(1000/frame_update);
}

MainWindow::~MainWindow()
{
    delete ui;
    delete player;
    delete boss;
}

void MainWindow::paintEvent(QPaintEvent* event)
{
    event->accept();
    QPainter painter {this};
    painter.drawPixmap(0, 0, snapshot);
}

void MainWindow::keyPressEvent(QKeyEvent* event)
{
    if (event->isAutoRepeat())
        return;
    switch (event->key()) {
    case key_transform:
        switch (state) {
        case State::keyboard:
            chosen_state = State::mouse;
            state = chosen_state;
            player->set_state(false);
            setMouseTracking(true);
            centralWidget()->setMouseTracking(true);
            break;
        case State::mouse:
            chosen_state = State::keyboard;
            player->set_state(true);
            setMouseTracking(false);
            centralWidget()->setMouseTracking(false);
            state = chosen_state;
            break;
        default:
            return;
        }
        break;
    case Qt::Key_Left:
        directs[static_cast<int>(Direct::left)] = true;
        break;
    case Qt::Key_Right:
        directs[static_cast<int>(Direct::right)] = true;
        break;
    case Qt::Key_Up:
        directs[static_cast<int>(Direct::up)] = true;
        break;
    case Qt::Key_Down:
        directs[static_cast<int>(Direct::down)] = true;
        break;
    case player_shoot:
        shoot = true;
        break;
    case Qt::Key_Space:
        if (state==State::over or state==State::win)
            start_game();
        break;
    case guide_key:
        guide();
        break;
    case help_key:
        emit help();
        pause();
        break;
    case key_pause:
        pause();
        break;
    case key_back:
        if (state == State::free) {
            state = pause_state;
            bgm.resume();
        }
        break;
    case key_quit:
        throw Quit_error{};
        break;
    default:
        if (state != State::free)
            switch (event->key()) {
            case key_practice:
                chosen_state = State::practice;
                start_game();
                break;
            case key_mokou:
                chosen_state = State::immortal;
                start_game();
                break;
            case key_ret_normal:
                chosen_state = State::keyboard;
                start_game();
                break;
            }
        break;
    }
}

void MainWindow::keyReleaseEvent(QKeyEvent* event)
{
    if (event->isAutoRepeat())
        return;
    switch (event->key()) {
    case Qt::Key_Left:
        directs[static_cast<int>(Direct::left)] = false;
        break;
    case Qt::Key_Right:
        directs[static_cast<int>(Direct::right)] = false;
        break;
    case Qt::Key_Up:
        directs[static_cast<int>(Direct::up)] = false;
        break;
    case Qt::Key_Down:
        directs[static_cast<int>(Direct::down)] = false;
        break;
    case player_shoot:
        shoot = false;
        break;
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent* event)
{
    player->mouse_move(event->pos());
}

void MainWindow::mousePressEvent(QMouseEvent* event)
{
    event->accept();
    if (state == State::mouse)
        shoot = true;
}

void MainWindow::mouseReleaseEvent(QMouseEvent* event)
{
    event->accept();
    if (state == State::mouse)
        shoot = false;
}

void MainWindow::frame()
{
    bg.update();
    fps.update_fps();
    if (state!=State::free) {
        pause_state = state;
        std::thread th_bullet {update_bullets,std::ref(bullets)};
        std::thread th_e_bullet {update_enemy_bullets,std::ref(enemy_bullets)};
        std::thread th_bomb {update_bombs,std::ref(bombs)};
        th_bullet.join();
        th_e_bullet.join();
        th_bomb.join();
        if (state!=State::bossdie and state!=State::win) {
            if (state != State::practice)
                set_enemy();
            if (boss_action())
                state = State::bossdie;
        }
        update_enemys();
        if (state != State::over) {
            if (shoot)
                player->shoot(bullets);
            if (state != State::mouse) {
                if (directs[static_cast<int>(Direct::left)])
                    player->move_left();
                if (directs[static_cast<int>(Direct::right)])
                    player->move_right();
                if (directs[static_cast<int>(Direct::up)])
                    player->move_up();
                if (directs[static_cast<int>(Direct::down)])
                    player->move_down();
            }
            if (player->collision(enemys, enemy_bullets, bombs) or boss->beat_player(player, bombs)) {
                if (chosen_state != State::immortal)
                    state = State::over;
                else
                    ++resurrection;
            }
            else if (state == State::bossdie)
                if (enemys.empty() and enemy_bullets.empty())
                    state = State::win;
        }
    }
    update_snapshot();
    update();
}

void MainWindow::start_game()
{
    if (!bgm.is_playing())
        bgm.play();
    state = chosen_state;
    boss_interval = boss_quit;
    bombs.clear();
    enemys.clear();
    bullets.clear();
    enemy_bullets.clear();
    directs &= 0;
    shoot = false;
    resurrection = 0;
    delete player;
    delete boss;
    player = new Object::Player {QRect{QPoint{0,0},bg.size()}};
    if (state == State::mouse) {
        player->set_state(false);
        setMouseTracking(true);
        centralWidget()->setMouseTracking(true);
    }
    boss = new Object::Boss {QRect{QPoint{0,0},bg.size()}};
    if (state == State::practice)
        boss->to_scene(0);
}

void MainWindow::set_level(int lev)
{
    bg = Map::Background{lev};
}

void MainWindow::update_snapshot()
{
    QPixmap temp_snap {bg_path.arg(1)};
    bg.draw_this(&temp_snap);
    if (state != State::over)
        player->draw_this(&temp_snap);
    enemys_draw(&temp_snap);
    bullets_draw(&temp_snap);
    bullets_e_draw(&temp_snap);
    for (const auto& b : bombs)
        b.draw_this(&temp_snap);
    boss->draw_this(&temp_snap);
    draw_condition(&temp_snap);
    snapshot = temp_snap;
}

void MainWindow::set_enemy()
{
    static int interval {enemy_show_itv};
    if (interval-- == 0) {
        interval = enemy_show_itv;
        Object::Enemy temp_enemy {QRect{QPoint{0,0},bg.size()}};
        enemys.append(temp_enemy);
    }
}

void MainWindow::update_enemys()
{
    for (auto i=enemys.begin(); i!=enemys.end(); )
        if ((*i).update_pos(bullets, bombs))
            i = enemys.erase(i);
        else {
            (*i).shoot(enemy_bullets);
            ++i;
        }
}

void MainWindow::update_enemy_bullets(QList<Object::Bullet>& enemy_bullets)
{
    for (auto i=enemy_bullets.begin(); i!=enemy_bullets.end(); )
        if ((*i).update_pos())
            i = enemy_bullets.erase(i);
        else
            ++i;
}

void MainWindow::update_bullets(QList<Object::Bullet>& bullets)
{
    for (auto i=bullets.begin(); i!=bullets.end(); )
        if ((*i).update_pos())
            i = bullets.erase(i);
        else
            ++i;
}

void MainWindow::update_bombs(QList<Bomb::Bomb>& bombs)
{
    for (auto i=bombs.begin(); i!=bombs.end(); )
        if ((*i).update())
            i = bombs.erase(i);
        else
            ++i;
}

void MainWindow::enemys_draw(QPixmap* pix)
{
    for (const auto& e : enemys)
        e.draw_this(pix);
}

void MainWindow::bullets_e_draw(QPixmap* pix)
{
    for (const auto& eb : enemy_bullets)
        eb.draw_this(pix);
}

void MainWindow::bullets_draw(QPixmap* pix)
{
    for (const auto& b : bullets)
        b.draw_this(pix);
}

void MainWindow::draw_condition(QPixmap* pix)
{
    QPainter painter {pix};
    painter.setFont(text_stt_font);
    if (state == State::win) {
        painter.setPen(QPen{(chosen_state==State::immortal) ? text_lose : text_win});
        painter.drawText(QRect{QPoint{0,0},bg.size()}, Qt::AlignCenter, tr("你赢了！\n%1按空格键重新开始").arg((chosen_state==State::immortal) ? "试着用普通的非无敌模式通过吧\n按下K键使用普通模式重开\n" : ""));
    }
    else if (state == State::over) {
        painter.setPen(QPen{text_lose});
        painter.drawText(QRect{QPoint{0,0},bg.size()}, Qt::AlignCenter, tr("你寄了！\n按空格键重新开始"));
    }
    painter.setPen(QPen{text_fps});
    painter.setFont(text_fps_font);
    painter.drawText(text_pos_fps, ((state==State::immortal) ? tr("%1 次复活").arg(resurrection) : tr("%1 fps").arg(fps.get_fps())));
    if (state == State::free) {
        painter.setBrush(QBrush{pause_bg_color});
        QRect draw_rect {QPoint{0,0},size()};
        painter.drawRect(draw_rect);
        painter.setFont(pause_font);
        painter.setPen(QPen{pause_font_color});
        painter.drawText(draw_rect, Qt::AlignCenter, pause_text);
    }
}

bool MainWindow::boss_action()
{
    if (boss_interval-- == 0) {
        boss_interval = boss_quit;
        boss->to_scene(player->get_rect().center().x());
    }
    else if (boss_interval==boss_quit-1 and state==State::practice) {
        ++boss_interval;
        boss->practice_update(enemy_bullets);
        return false;
    }
    return boss->update(bullets, enemy_bullets, bombs);
}

void MainWindow::pause()
{
    bgm.pause();
    state = State::free;
}

void MainWindow::guide()
{
    path dir {current_path()};
    ShellExecuteA(nullptr, nullptr, (dir.string()+guide_path).c_str(), nullptr, nullptr, SW_SHOW);
    pause();
}
